﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.IO.Ports;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using Walson.Net.Sockets;

namespace Walson.IO.Adapter
{
    /// <summary>
    /// UDP-串口桥接器
    /// </summary>
    public class UdpSerialBridge : Component
    {
        private UdpClientCompact2 _udp;
        private SerialPort _sp;
        private bool _terminate = false;
        UdpClientCompact2.DataReceivedHandler _udpDataReceivedHandler;
        SerialDataReceivedEventHandler _serialDataReceivedEventHandler;
        System.Timers.Timer _udpPackageTimer = new System.Timers.Timer(100.0);
        MemoryStream _SerialDataBuffer = new MemoryStream();
        bool _packaging = false;

        /// <summary>
        /// 桥接器的 IP 地址
        /// </summary>
        public string IpAddress
        {
            get
            {
                return _udp.Address;
            }
            set
            {
                //_udp.Address = value;
                _udp.Set(value, _udp.Port);
            }
        }

        /// <summary>
        /// 桥接器的端口号
        /// </summary>
        public int UdpPort
        {
            get
            {
                return _udp.Port;
            }
            set
            {
                //_udp.Port = value;
                _udp.Set(_udp.Address, value);
            }
        }

        /// <summary>
        /// 桥接器的串口号
        /// </summary>
        public string SerialPortName
        {
            get
            {
                return _sp.PortName;
            }
            set
            {
                _sp.PortName = value;
            }
        }

        /// <summary>
        /// 串口的波特率
        /// </summary>
        public int BaudRate
        {
            get
            {
                return _sp.BaudRate;
            }
            set
            {
                _sp.BaudRate = value;
            }
        }

        /// <summary>
        /// 上位机的 IP 地址
        /// </summary>
        public string RemoteIp
        {
            get;
            set;
        }

        /// <summary>
        /// 上位机的端口号
        /// </summary>
        public int RemotePort
        {
            get;
            set;
        }

        /// <summary>
        /// 串口数据组包最少字节数。当数据等于或大于此值时，立刻组包从以太发送，而不管 UdpPacketMaxTime 是否到达。
        /// </summary>
        public long UdpPacketMinSize
        {
            get;
            set;
        }

        /// <summary>
        /// 数据组包的最大等待时间（毫秒）。当等待时间到达该值时，即便数据量没有达到 UdpPacketMinSize 所指示的大小，也仍然组包发送。
        /// </summary>
        public double UdpPacketMaxTime
        {
            get
            {
                return _udpPackageTimer.Interval;
            }
            set
            {
                _udpPackageTimer.Interval = value;
            }
        }

        public bool IsRunning
        {
            get;
            set;
        }

        /// <summary>
        /// 创建桥接器，所有的参数将被置为默认值
        /// </summary>
        public UdpSerialBridge()
        {
            _udp = new UdpClientCompact2();
            _sp = new SerialPort();
            _udpDataReceivedHandler = new UdpClientCompact2.DataReceivedHandler(_udp_DataReceived);
            _serialDataReceivedEventHandler = new SerialDataReceivedEventHandler(_sp_DataReceived);
            _udpPackageTimer.Elapsed += new System.Timers.ElapsedEventHandler(_udpPackageTimer_Elapsed);
            UdpPacketMinSize = 128;
            UdpPacketMaxTime = 100;
            RemoteIp = _udp.Address;
            RemotePort = 10000;
        }

       
        /// <summary>
        /// 创建桥接器
        /// </summary>
        /// <param name="ipAddress">桥接器的 IP 地址</param>
        /// <param name="port">桥接器的端口号</param>
        /// <param name="serialPortName">串口的端口号</param>
        /// <param name="baudRate">串口的波特率</param>
        /// <param name="remoteIp">上位机的 IP 地址</param>
        /// <param name="remotePort">上位机的端口号</param>
        public UdpSerialBridge(string ipAddress, int port, string serialPortName,int baudRate,string remoteIp,int remotePort)
        {
            _udp = new UdpClientCompact2(ipAddress, port);
            _sp = new SerialPort(serialPortName, baudRate);
            _udpDataReceivedHandler = new UdpClientCompact2.DataReceivedHandler(_udp_DataReceived);
            _serialDataReceivedEventHandler = new SerialDataReceivedEventHandler(_sp_DataReceived);
            _udpPackageTimer.Elapsed += new System.Timers.ElapsedEventHandler(_udpPackageTimer_Elapsed);
            UdpPacketMinSize = 128;
            UdpPacketMaxTime = 100;
            RemoteIp = remoteIp;
            RemotePort = remotePort;
        }

        /// <summary>
        /// 创建桥接器
        /// </summary>
        /// <param name="serialPortName">串口的端口号</param>
        /// <param name="baudRate">串口的波特率</param>
        /// <param name="remoteIp">上位机的 IP 地址</param>
        /// <param name="remotePort">上位机的端口号</param>
        public UdpSerialBridge(string serialPortName, int baudRate,string remoteIp,int remotePort)
        {
            _udp = new UdpClientCompact2();
            _sp = new SerialPort(serialPortName, baudRate);
            _udpDataReceivedHandler = new UdpClientCompact2.DataReceivedHandler(_udp_DataReceived);
            _serialDataReceivedEventHandler = new SerialDataReceivedEventHandler(_sp_DataReceived);
            _udpPackageTimer.Elapsed += new System.Timers.ElapsedEventHandler(_udpPackageTimer_Elapsed);
            UdpPacketMinSize = 128;
            UdpPacketMaxTime = 100;
            RemoteIp = remoteIp;
            RemotePort = remotePort;
        }

        /// <summary>
        /// 创建桥接器
        /// </summary>
        /// <param name="port">桥接器的端口号</param>
        /// <param name="serialPortName">串口的端口号</param>
        /// <param name="baudRate">串口的波特率</param>
        public UdpSerialBridge(int port,string serialPortName, int baudRate)
        {
            _udp = new UdpClientCompact2(port);
            _sp = new SerialPort(serialPortName, baudRate);
            _udpDataReceivedHandler = new UdpClientCompact2.DataReceivedHandler(_udp_DataReceived);
            _serialDataReceivedEventHandler = new SerialDataReceivedEventHandler(_sp_DataReceived);
            _udpPackageTimer.Elapsed += new System.Timers.ElapsedEventHandler(_udpPackageTimer_Elapsed);
            UdpPacketMinSize = 128;
            UdpPacketMaxTime = 100;
            RemoteIp = _udp.Address;
            RemotePort = 10000;
        }

        

        /// <summary>
        /// 绑定指定的 UDP 和串口
        /// </summary>
        /// <param name="udp">指定的 UDP</param>
        /// <param name="serialPort">指定的串口</param>
        public void Bind(UdpClientCompact2 udp,SerialPort serialPort)
        {
            Stop();

            if (_udp != null)
            {
                _udp.Dispose();
                _udp = null;
            }
            
            _udp = udp;
        }

        /// <summary>
        /// 启动桥接器
        /// </summary>
        public void Start()
        {
            if (IsRunning)
                return;

            _terminate = false;
            _udp.Clear(); 
            _udp.DataReceived += _udpDataReceivedHandler;
            _sp.DataReceived += _serialDataReceivedEventHandler;

            _udp.StartAutoReceive();
            _sp.Open();

            _udpPackageTimer.Enabled = true;
            IsRunning = true;
        }

        /// <summary>
        /// 停止桥接器
        /// </summary>
        public void Stop()
        {
            if (!IsRunning)
                return;

            _udpPackageTimer.Enabled = false;
            _terminate = true;
            _udp.DataReceived -= _udpDataReceivedHandler;
            _sp.DataReceived -= _serialDataReceivedEventHandler;

//             if (_udp.IsRunning)
//                 _udp.StopAutoRecieve();

            if (_sp.IsOpen)
                _sp.Close();

            _SerialDataBuffer.Position = 0;
            _SerialDataBuffer.SetLength(0);           

            IsRunning = false;
        }

        private void _sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (_terminate || _packaging)
                return;

            byte[] buffer=new byte[_sp.BytesToRead];
            _sp.Read(buffer, 0, buffer.Length);
            _SerialDataBuffer.Write(buffer, 0, buffer.Length);
            _udpPackageTimer.Enabled = true;

            if (_SerialDataBuffer.Length >= UdpPacketMinSize)
            {
                _packaging = true;
                buffer = _SerialDataBuffer.ToArray();
                _SerialDataBuffer.Position = 0;
                _SerialDataBuffer.SetLength(0);
                _packaging = false;
                _udp.SendTo(RemoteIp, RemotePort, buffer);                
            }

            //_SerialDataBuffer
        }

        private void _udp_DataReceived(object sender, EventArgs e)
        {
            if (_terminate)
                return;

            UdpClientCompact2.MessageInfo msg = _udp.ReadMessage();
            _sp.Write(msg.MessageBody, 0, msg.MessageBody.Length);
        }

        private void _udpPackageTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            _udpPackageTimer.Enabled = false;
            if (_SerialDataBuffer.Length == 0)
                return;

            byte[] buffer;
            _packaging = true;
            buffer = _SerialDataBuffer.ToArray();
            _SerialDataBuffer.Position = 0;
            _SerialDataBuffer.SetLength(0);
            _packaging = false;
            _udp.SendTo(RemoteIp, RemotePort, buffer);
            
        }
    }

    /// <summary>
    /// UDP 串口服务器。端口从20000开始，分别绑定到第 n 个串口,n=1,2,3,...
    /// </summary>
    public class UdpSerialServer : Component
    {
        // 9090 端口是配置端口
        UdpClientCompact m_configChannel = new UdpClientCompact(9090);
        List<UdpSerialBridge> m_udpSerialParis = new List<UdpSerialBridge>();

        public string[] SerialPortNames
        {
            get
            {
                return SerialPort.GetPortNames();
            }
        }

        public UdpSerialServer()
        {
            m_configChannel.DataReceived += new UdpClientCompact.DataReceivedHandler(m_configChannel_DataReceived);
            string[] serials = SerialPortNames;
            UdpSerialBridge[] bridges = new UdpSerialBridge[serials.Length];
            int index=0;
            foreach (string com in serials)
            {
                bridges[index] = new UdpSerialBridge(20000 + index, com, 9600);
                
                index++;
            }
            m_udpSerialParis.AddRange(bridges);
            
        }

        public void Start()
        {
            foreach (UdpSerialBridge bridge in m_udpSerialParis)
            {
                bridge.Start();
            }

            //m_configChannel.StartAutoReceive();
        }

        void m_configChannel_DataReceived(object sender, EventArgs e)
        {
            // 例子： SET COM1 BAUD1 UDP1 LOCAL_PORT1 REMOTEIP REMOTEPORT
            // 
            UdpClientCompact.MessageInfo msg=m_configChannel.ReadMessage();
            string[] command = Encoding.Default.GetString(msg.MessageBody).Split(' ');

            switch (command[0].ToLower())
            {
                case "help":
                    {
                        string str = "帮助\r\n" +
                                    "设置串口：SET 串口名称 波特率 端口号 上位机IP地址 上位机端口号 UDP最小组包长度 最大组包等待时间\r\n" +
                                    "例如：SET COM1 9600 20000 192.168.1.100 10000 100 100\r\n" +
                                    "表示设置 COM1 的波特率为9600，来自 COM1 数据将被送到 192.168.1.100：8080，发往串口服务器 20000 端口的数据将被送到 COM1\r\n" +
                                    "并且，当接收到串口数据大于等于 100 字节或超过 100 毫秒没有接收到数据时，将自动组包发送到 UDP"+
                                    "获取串口列表：GET COMLIST\r\n";
                                    //+"获取UDP列表：GET UDPLIST\r\n";
                        m_configChannel.SendTo(msg.SenderInfo.Address.ToString(), msg.SenderInfo.Port, str);
                        break;
                    }
                case "set":
                    {
                        if (command.Length != 8)
                            break;

                        bool comPortExist = false;
                        string comPort = command[1];
                        int localPort;
                        int baudRate;
                        IPAddress remoteIp;
                        int remotePort;
                        int packetSize;
                        int waitTime;

                        foreach (string com in SerialPortNames)
                        {
                            if (com.ToLower() == comPort.ToLower())
                            {
                                comPortExist = true;
                                break;
                            }
                        }

                        if (comPortExist == false)
                            break;

                        if (int.TryParse(command[2], out baudRate) == false)
                            break;

                        if (int.TryParse(command[3], out localPort) == false)
                            break;

                        if (IPAddress.TryParse(command[4], out remoteIp) == false)
                            break;

                        if (int.TryParse(command[5], out remotePort) == false)
                            break;

                        if (int.TryParse(command[6], out packetSize) == false)
                            break;

                        if (int.TryParse(command[7], out waitTime) == false)
                            break;

                        foreach (UdpSerialBridge bridge in m_udpSerialParis)
                        {
                            if (bridge.SerialPortName.ToLower() == comPort)
                            {
                                bridge.Stop();
                                bridge.RemoteIp = remoteIp.ToString();
                                bridge.RemotePort = remotePort;
                                bridge.BaudRate = baudRate;
                                bridge.UdpPort = localPort;
                                bridge.UdpPacketMaxTime = waitTime;
                                bridge.UdpPacketMinSize = packetSize;
                                bridge.Start();
                                break;
                            }
                        }

                        break;
                    }
                case "get":
                    {
                        switch (command[1].ToLower())
                        {
                            case "comlist":
                                {
                                    StringBuilder sb = new StringBuilder();
                                    sb.AppendLine("串口号\t波特率\t对应的 UDP 端口号\t上位机 IP\t上位机端口号\t最小组包长度\t最大等待时间");
                                    foreach (UdpSerialBridge bridge in m_udpSerialParis)
                                    {
                                        sb.AppendLine(bridge.SerialPortName + "\t" +
                                                      bridge.BaudRate.ToString() + "\t" +
                                                      bridge.UdpPort.ToString() + "\t\t\t" +
                                                      bridge.RemoteIp + "\t" +
                                                      bridge.RemotePort + "\t\t" +
                                                      bridge.UdpPacketMinSize.ToString() + "\t\t" +
                                                      bridge.UdpPacketMaxTime.ToString()
                                                      );
                                    }
                                    m_configChannel.SendTo(msg.SenderInfo.Address.ToString(), msg.SenderInfo.Port, sb.ToString());
                                    break;
                                }
                        }
                        break;
                    }
                case "discovery":
                    {
                        if (command[1] == null)
                            return;

                        m_configChannel.SendTo(msg.SenderInfo.Address.ToString(), msg.SenderInfo.Port, "DISCOVERY_RESPONSE " + command[1]);
                        break; 
                    }
                default:
                    {
                        break;
                    }
            }

        }

    }
}
